package raft

import (
	"math/rand"
	"time"
)

// 选举Looper中实现超时检查
func (rf *Raft) resetElectionTimerLocked() {
	// 设置选举间隔时间
	rf.electionStart = time.Now()
	randRange := int64(electionTimeOutMax - electionTimeOutMin)
	rf.electionTimeOut = electionTimeOutMin + time.Duration(rand.Int63()%randRange)
}

func (rf *Raft) isElectionTimeoutLocked() bool{
	return time.Since(rf.electionStart) >= rf.electionTimeOut
}
// 以上完成 选举逻辑中 选举循坏的第一部分

func (rf *Raft) isMoreUpToDateLocked(candidateIndex, candidateTerm int) bool {
	// 判断上一条日志和候选者谁更新
	lastIndex , lastTerm := rf.log.last()
	LOG(rf.me, rf.currentTerm, DVote, "Compare last log, Me: [%d]T%d, Candidate: [%d]T%d", lastIndex, lastTerm, candidateIndex, candidateTerm)
	if lastTerm != candidateTerm {
		return lastTerm > candidateTerm
	}
	return lastIndex > candidateIndex
}
// example RequestVote RPC arguments structure.
// field names must start with capital letters!
type RequestVoteArgs struct {
	// Your data here (PartA, PartB).
	Term			int
	CandidateId		int
	LastLogIndex	int
	LastLogTerm		int
}

// example RequestVote RPC reply structure.
// field names must start with capital letters!
type RequestVoteReply struct {
	// Your data here (PartA).
	Term		int
	VoteGranted	bool
}

// example RequestVote RPC handler.
func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {
	// Your code here (PartA, PartB).
	// 任期对齐
	rf.mu.Lock()
	defer rf.mu.Unlock()
	reply.Term = rf.currentTerm
	reply.VoteGranted = false
	if rf.currentTerm > args.Term {
		// 拒绝低任期的请求
		LOG(rf.me, rf.currentTerm, DVote, "-> S%d, Reject vote, higher term, T%d>T%d", args.CandidateId, rf.currentTerm, args.Term)
		return
	}
	if rf.currentTerm < args.Term {
		rf.becomeFollowerLocked(args.Term)
	}
	//检查投票
	if rf.votedFor != -1 && rf.votedFor != args.CandidateId {
		LOG(rf.me, rf.currentTerm, DVote, "-> S%d, Reject Voted, Already voted to S%d", args.CandidateId, rf.votedFor)
		return
	}

// check log, only grante vote when the candidates have more up-to-date log
	if rf.isMoreUpToDateLocked(args.LastLogIndex,args.LastLogTerm) {
		LOG(rf.me, rf.currentTerm, DVote, "-> S%d, Reject Vote, S%d's log less up-to-date", args.CandidateId)
		return
	}

	reply.VoteGranted = true
	rf.votedFor = args.CandidateId
	rf.persistLocked()
	rf.resetElectionTimerLocked()
	LOG(rf.me, rf.currentTerm, DVote, "-> S%d", args.CandidateId)
}

// example code to send a RequestVote RPC to a server.
// server is the index of the target server in rf.peers[].
// expects RPC arguments in args.
// fills in *reply with RPC reply, so caller should
// pass &reply.
// the types of the args and reply passed to Call() must be
// the same as the types of the arguments declared in the
// handler function (including whether they are pointers).
//
// The labrpc package simulates a lossy network, in which servers
// may be unreachable, and in which requests and replies may be lost.
// Call() sends a request and waits for a reply. If a reply arrives
// within a timeout interval, Call() returns true; otherwise
// Call() returns false. Thus Call() may not return for a while.
// A false return can be caused by a dead server, a live server that
// can't be reached, a lost request, or a lost reply.
//
// Call() is guaranteed to return (perhaps after a delay) *except* if the
// handler function on the server side does not return.  Thus there
// is no need to implement your own timeouts around Call().
//
// look at the comments in ../labrpc/labrpc.go for more details.
//
// if you're having trouble getting RPC to work, check that you've
// capitalized all field names in structs passed over RPC, and
// that the caller passes the address of the reply struct with &, not
// the struct itself.
func (rf *Raft) sendRequestVote(server int, args *RequestVoteArgs, reply *RequestVoteReply) bool {
	ok := rf.peers[server].Call("Raft.RequestVote", args, reply)
	return ok
}

func (rf *Raft) startElection(term int) bool {
	votes := 0
	askVoteFromPeer := func(peer int, args *RequestVoteArgs) {
		// send rpc to `peer` and handle the response
		reply := &RequestVoteReply{}
		ok := rf.sendRequestVote(peer, args, reply)
		
		rf.mu.Lock()
        defer rf.mu.Unlock()
		if !ok {
			return 
		}
		if rf.contextLostLocked(Candidate, term) {
			return 
		}
		if reply.Term > rf.currentTerm {
			rf.becomeFollowerLocked(reply.Term)
			return
		}
		if reply.VoteGranted {
			votes++
		}
		if votes > len(rf.peers) / 2 {
			rf.becomeLeaderLocked()
			go rf.replicationTicker(term) // 发送心跳或者复制日志
		}
	}
	rf.mu.Lock()
	defer rf.mu.Unlock()

	// every time locked 
	if rf.contextLostLocked(Candidate, term) {
		return false
	}
	lastIdx, lastTerm := rf.log.last()
	for peer := 0; peer < len(rf.peers); peer++ {
			if peer == rf.me { 
				votes++
				continue
			}

			args := &RequestVoteArgs{
				Term:        term,
				CandidateId: rf.me,
				LastLogIndex: lastIdx,
				LastLogTerm: lastTerm,
			}
			go askVoteFromPeer(peer, args)
	}

	return true
}
func (rf *Raft) electionTicker() {
	for !rf.killed() {

		// Your code here (PartA)
		// Check if a leader election should be started.

		// pause for a random amount of time between 50 and 350
		// milliseconds.
		rf.mu.Lock();

		// 用到rf全局变量的就要用锁
		if rf.role != Leader && rf.isElectionTimeoutLocked() {
			rf.becomeCandidateLocked()
			go rf.startElection(rf.currentTerm)
			// go + 函数 代表用一个新的协程调用 避免堵塞的情况
		}
		rf.mu.Unlock()
		ms := 50 + (rand.Int63() % 300)
		time.Sleep(time.Duration(ms) * time.Millisecond)
	}
}